%%%%%%%%%%%%%%%%%%%%%%%
%%
%% Module summary
%%
%% Main authors: 
%% Progress to usefulness: 
%%
%% The more aggressive set-up process
%% described in the original Chord paper. 
%%
%%%%%%%%%%%%%%%%%%%%%%%

-module(chord_aggressive).
-include("db.hrl").
-compile(export_all).

-import(chord_lib, [dbg_str/2, dbg_format/3, dbg_header/2]).
-import(chord_lib, [dbg_msg/2, dbg_msg_loc/2]).
-import(chord_lib, [get_node/1, get_pid/1, get_position/1]).
-import(chord_lib, [get_finger/2, set_finger/3, finger_start/2, get_M/0, modspace/1]).

%%%%%%%%%%%%%%%%%%%%%
%%
%% Action wrappers
%%
%%%%%%%%%%%%%%%%%%%%%

%% Getters: 
%% Either performs the get on ourself, and return the value,
%% or call a remote node, and return the remote node's value. 

%% Setters: 
%% Either performs the update on ourself, and return the updated state,
%% or call a remote node, and return our old state. 

wrap_update_finger_table(State, To, S, I) ->
    case To#node.node == node() of
      true ->
        dbg_msg_loc("update_finger_table", State),
        update_finger_table(S, I, State);
      false ->
        chord_lib:rem_query_cast(To, {update_finger_table, S, I}, State),
        State
    end.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%% Functions for creating the node and state,
%% and for updating the state. 
%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Initialize a new node. This part is run in the child process, 
%% and should never be run by any parent. 
init(_Params) ->
  State = chord_lib:create_state(),
  io:fwrite("Node ~p:~p has been spawned at position ~B.~n",
            [get_node(State), get_pid(State), get_position(State)]),
  {ok, State}.

%% Assigns a position to a Chord node. 
%% Returns the updated state. 
assign(Pos, State) ->
  Self  = State#chord.identity,
  Self2 = Self#node{ position = Pos },
  State2 = State#chord{ identity = Self2 },
  io:fwrite("Node ~p:~p has been re-assigned to position ~B.~n",
            [get_node(State2), get_pid(State2), get_position(State2)]),
  State2.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%% Functions for joining the identifier circle.
%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Set up the first node in the Chord ring. 
start_ring(State) ->
  Id = State#chord.identity,
  Ft2 = chord_lib:fill_finger_table(Id),
  State2 = State#chord{finger_table = Ft2, predecessor = Id},
  io:fwrite("Node ~p:~p has started the circle.~n",
            [get_node(State2), get_pid(State2)]),
  State2.

%% Tell node n that it has joined the ring, 
%% and that node n' already is present in the ring. 
%%
%% If n' is the atom "nothing", this node is the first node 
%% in the circle. 
%%
%% Returns the updated state. 
%%
%% Let n learn about it's surroundings, build its state,
%% and tell other nodes about its presence. 
join(N2name, State) ->
  N2 = chord_lib:create_node(N2name),
  dbg_header("Initializing finger table...", State),
  State2 = init_finger_table(N2, State),
  dbg_header("Updating other nodes...", State),
  State3 = update_others(State2),
  io:format("Node ~p:~p has joined the circle~n",
            [get_node(State3), get_pid(State3)]),
  State3.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%% Functions for setting up our own finger table
%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Initialize the finger table for node n, given that we already
%% know that node n' is in the Chord ring. 
%% Returns the updated state. 
init_finger_table(N2, State) ->
  Id  = State#chord.identity,

  %% Find the first finger node, and save to the finger table.
  dbg_header("Setting up our successor...", State),
  Fs  = finger_start(1, State),
  Suc = chord_lib:wrap_find_successor(State, N2, Fs),
  dbg_format("Got successor: ~w~n", [Suc], State),
  State2 = set_finger(1, Suc, State),

  %% Find the predecessor to our first finger, 
  %% and make this our predecessor.
  dbg_header("Setting up our predecessor...", State),
  Pred = chord_lib:wrap_get_predecessor(State2, Suc),
  dbg_format("Got predecessor: ~w~n",[Pred], State),
  State3 = State2#chord{ predecessor = Pred },

  %% Set the successor's predecessor to our node.
  dbg_header("Updating our successor's predecessor...", State),
  chord_lib:wrap_set_predecessor(State3, Suc, Id),

  dbg_header("Initializing finger table...", State),
  init_fingers_i(1, N2, State3).

%% Recurse into the finger table, and set up each entry. 
%% Returns the updated state. 
init_fingers_i(I, N2, State) ->
  case I >= get_M() of
    true  -> State;
    false ->
        State2 = init_finger_i(I, N2, State),
        init_fingers_i(I + 1, N2, State2)
  end.

%% Set up a single finger table entry. 
%% Returns the updated state. 
init_finger_i(I, N2, State) ->
  case I >= get_M() of
    true  -> error("init_finger: Invalid finger number");
    false ->
        SelfPos   = get_position(State),
        NextStart = finger_start(I + 1, State),
        Fing      = get_finger(I, State),
        FingPos   = Fing#node.position,
        %case (SelfPos =< NextStart) and (NextStart < FingPos) of
        case intmath:interval_check_inc_exc(SelfPos, NextStart, FingPos) of
          true  ->
              set_finger(I + 1, Fing, State);
          false ->
              NextFing = chord_lib:wrap_find_successor(State, N2, NextStart),
              set_finger(I + 1, NextFing, State)
        end
    end.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%% Functions for updating the other nodes about our presence
%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Tell other nodes about node n's presence in the Chord ring. 
%% Might affect our state, and returns the updated state. 
update_others(State) ->
  update_others_i(1, State).

%% Recurse and tell the remaining other nodes 
%% about node n's presence in the Chord ring. 
%% Might affect our state, and returns the updated state. 
update_others_i(I, State) ->
  case I > get_M() of
    true  -> State;
    false ->
        State2 = update_other_i(I, State),
        update_others_i(I + 1, State2)
  end.

%% Tell a single other node about node n's presence in the Chord ring. 
%% Might affect our state, and returns the updated state. 
update_other_i(I, State) ->
  dbg_str("///////////////////////////////~n", State),
  dbg_format("Updating other, index = ~B~n", [I], State),
  case I > get_M() of
    true  -> error("update_other_i: Invalid index");
    false ->
        Id   = State#chord.identity,
        NPos = Id#node.position,
        Pos  = modspace(NPos - intmath:pow(2, I - 1)),
        dbg_format("update_other_i: Looking up pred for pos ~B~n", [Pos], State),
        %% Don't use chord_distributed for this, since it would block and time out. 
        %% Always use chord_iterative. 
        P    = chord_iterative:find_predecessor(Pos, State),
        wrap_update_finger_table(State, P, Id, I)
  end.

%% We have been notified of node S's presence in the Chord ring. 
%% Node S is a candidate for our Ith finger table entry. 
%% Investigate if S should be our new finger or not. 
%% If yes, pass this information on. 
%%
%% Returns the updated state. 
update_finger_table(S, I, State) ->
  %%NPos = get_position(State),
  SPos = S#node.position,
  Fing = get_finger(I, State),
  FingPos = Fing#node.position,
  MyFingerStart = finger_start(I, State),
  %case (NPos =< SPos) and (SPos < FingPos) of
  % 
  % Check if the new node lies between finger_start and the current
  % node at finger position
  case intmath:interval_check_inc_exc(MyFingerStart, SPos, FingPos) of
    true  ->
        dbg_format("Updating our finger nr ~B with node ~p at pos ~B~n",
                   [I, S#node.node, S#node.position], State),
        State2 = set_finger(I, S, State),
        P = State#chord.predecessor,
        % UPDATE: update_finger calls are now asynchronous
        % This call leads to deadlock if P == S
        wrap_update_finger_table(State2, P, S, I);
    false ->
        dbg_format("Ignoring request to update our finger nr ~B with node ~p at pos ~B, in favor of old node ~p at position ~B~n",
                   [I, S#node.node, S#node.position, Fing#node.node, Fing#node.position], State),
        State
  end.

